package com.itheima.advice;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.sql.DataSource;

/**
 * @author by itheima
 * @Date 2022/4/24
 * @Description 定义事务管理的切面类
 */
//@Component
//@Aspect//定义当前类为切面类
public class TxAdvice {

    /**
     * 注入数据源
     */
    @Autowired
    private DataSource dataSource;

    @Pointcut("execution(* com.itheima.service.impl.AccountServiceImpl.transfer(..))")
    public void myPt(){}


    /**
     * 定义事务的环绕通知
     * @param pjp
     * @return
     */
    @Around("myPt()")
    public Object txAround(ProceedingJoinPoint pjp){
        //1.构建事务管理器，管理事务:提交 回滚
        DataSourceTransactionManager dstm = new DataSourceTransactionManager();
        //设置数据源
        dstm.setDataSource(dataSource);
        //2.构建事务定义对象:设置隔离级别 传播行为 超时时间 等
        DefaultTransactionDefinition td = new DefaultTransactionDefinition();
        //设置为可重复读隔离级别，保证与mysql一致 》 读未提交
        /*
            mysql默认隔离级别：可重复读
            oracle默认隔离级别：读已提交
         */
        td.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
        //设置事务的超时时间
        /*
            事务超时时间，参数默认是-1，表示永不超时（什么时候结束，则结束）
            单位是：秒
         */
        td.setTimeout(10);
        //设置是否为只读事务
        /*
            设置当前事务是否是只读事务
                true：表示只读事务（事务内的操作，只有查询，没有增删改操作）
                false:当前操作存在写操作；
         */
        td.setReadOnly(false);
        //设置事务的传播特性 TODO
        td.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        //3.获取事务状态对象 开启事务
        TransactionStatus ts = dstm.getTransaction(td);
        Object result=null;
        try {
             result = pjp.proceed();
            //提交事务
            dstm.commit(ts);
        } catch (Throwable e) {
            //回滚事务
            dstm.rollback(ts);
        }
        return result;
    }
}
